/*
  Part of the MasterTool3D project - http://code.google.com/p/master-tool-3d

  Copyright (c) 2011 Ingo Pueschl

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation, version 2.1.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General
  Public License along with this library; if not, write to the
  Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  Boston, MA  02111-1307  USA
*/

package techjit.gfx.core;

import java.awt.Point;
import java.util.Date;

import processing.core.PApplet;
import processing.core.PMatrix3D;
import processing.core.PVector;
import techjit.gfx.util.CoordinateSystemDrawer;
import techjit.gfx.util.PU;
import techjit.gfx.util.geo.Dimensions;
import techjit.gfx.util.geo.Rotation;
import techjit.gfx.util.geo.ScreenVector;
import techjit.gfx.util.geo.Vector;
import techjit.util.LogManager;
import techjit.util.RuntimeParams;

public abstract class GFX_SceneElement extends GFX_Object {

	// FIELDS
	// body
	protected GFX_Object parentObject;
	private GFX_Scene rootScene;
	public PMatrix3D rotationMatrix = new PMatrix3D();
	private Vector rotationOffset = new Vector(0);

	protected Dimensions localDims = new Dimensions(2, 2.3f, 2.7f);

	// CONSTRUCTORS
	public GFX_SceneElement(GFX_Scene rootScene) {
		// head
		super();

		// body
		this.rootScene = rootScene;
	}

	// METHODS
	// public void addToRotation(Rotation rotation) {
	// this.rotation.add(rotation);
	// }

	protected void arrange() {
		float width = localDims.getWidth();
		float height = localDims.getHeight();
		float depth = localDims.getDepth();

		PVector v000 = new PVector(0, 0, 0);
		PVector v001 = new PVector(0, 0, depth);
		PVector v010 = new PVector(0, height, 0);
		PVector v011 = new PVector(0, height, depth);
		PVector v100 = new PVector(width, 0, 0);
		PVector v101 = new PVector(width, 0, depth);
		PVector v110 = new PVector(width, height, 0);
		PVector v111 = new PVector(width, height, depth);

		v000 = draw_applyMatrixToVector(rotationMatrix, v000);
		v001 = draw_applyMatrixToVector(rotationMatrix, v001);
		v010 = draw_applyMatrixToVector(rotationMatrix, v010);
		v011 = draw_applyMatrixToVector(rotationMatrix, v011);
		v100 = draw_applyMatrixToVector(rotationMatrix, v100);
		v101 = draw_applyMatrixToVector(rotationMatrix, v101);
		v110 = draw_applyMatrixToVector(rotationMatrix, v110);
		v111 = draw_applyMatrixToVector(rotationMatrix, v111);

		PVector[] vectors = { v000, v001, v010, v011, v100, v101, v110, v101,
				v111 };

		float minX = 0, minY = 0, minZ = 0, maxX = 0, maxY = 0, maxZ = 0;
		for (PVector vector : vectors) {
			minX = PApplet.min(vector.x, minX);
			minY = PApplet.min(vector.y, minY);
			minZ = PApplet.min(vector.z, minZ);
			maxX = PApplet.max(vector.x, maxX);
			maxY = PApplet.max(vector.y, maxY);
			maxZ = PApplet.max(vector.z, maxZ);
		}
		relDims = new Dimensions(PApplet.abs(minX) + maxX, PApplet.abs(minY)
				+ maxY, PApplet.abs(minZ) + maxZ);
		rotationOffset = new Vector(PApplet.abs(minX), PApplet.abs(minY),
				PApplet.abs(minZ));
	}

	protected void changeLocalDimensions(Dimensions localDims) {
		this.localDims.change(localDims);
	}

	protected void changeParent(GFX_Object parentObject) {
		this.parentObject = parentObject;
	}

	public void changeRotation(Rotation rotation) {
		rotationMatrix.preApply(PU.getRotationMatrixX(rotation.getAlpha()));
		rotationMatrix.preApply(PU.getRotationMatrixY(rotation.getBeta()));
		rotationMatrix.preApply(PU.getRotationMatrixZ(rotation.getGamma()));

		// this.rotation.change(rotation);
	}

	public void choose(float mouseX, float mouseY) {
		if (this instanceof GFX_Node) {

			techjit.home.elena.HomeER homeER = new techjit.home.elena.HomeER();

			if (homeER.isPointInBox(((GFX_Node) this).screenPos, new Point(
					(int) mouseX, (int) mouseY))) {
				if (rootScene.getCurrentSceneElement() instanceof GFX_Node
						&& homeER
								.isPointInBox(((GFX_Node) rootScene
										.getCurrentSceneElement()).screenPos,
										new Point((int) mouseX, (int) mouseY))) {
					if (((GFX_Node) this).modelZ515 > ((GFX_Node) rootScene
							.getCurrentSceneElement()).modelZ515) {
						rootScene.changeCurrentSceneElement(this);
					}
				} else {
					rootScene.changeCurrentSceneElement(this);
				}
			}
		} else if (this instanceof GFX_Layout) {
			for (GFX_SceneElement element : ((GFX_Layout) this).elements) {
				element.choose(mouseX, mouseY);
			}
		}
	}

	@Override
	protected void draw(PApplet p) {

		// head
		super.draw(p);
		// absPos = PU.model(p);

		// translate local origin by rotation offset
		draw_translateByRotationOffset(p);

		// rotate local coordinate system
		// draw_rotateLocalCoordinateSystem(p);
		// LM.debugIP("apply in draw");
		p.applyMatrix(rotationMatrix);

		//

		//
		draw_drawRotationOffsetBox(p);

		// local bounding box
		draw_drawLocalBoundingBox(p);

		// local coordinate system
		draw_drawLocalCoordinateSystem(p);

		// <>
		// ==>
		if (rootScene.isCurrentSceneElement(this)) {
			rootScene
					.change_currentSceneElement_screenPosition(new ScreenVector(
							(int) p.screenX(0, 0, 0), (int) p.screenY(0, 0, 0)));
		}
		// </>
	}

	private PVector draw_applyMatrixToVector(PMatrix3D matrix, PVector oldVector) {
		PVector newVector = new PVector();
		matrix.mult(oldVector, newVector);
		return newVector;
	}

	private void draw_drawLocalBoundingBox(PApplet p) {
		p.pushStyle();
		{
			if (isCurrentSceneElement() && RuntimeParams.SHOW__CURRENT) {
				p.pushStyle();
				{
					int branch = 1;
					switch (branch) {
					case 0:
						p.fill(0xFF00FFFF);
						p.noStroke();
						break;
					case 1:
						p.noFill();
						p.stroke(RuntimeParams.COLOR__CYAN);
						p.strokeWeight(3);
						break;
					}
					PU.box(p, getLocalPosition(new Vector(-.1f)),
							getLocalPosition(new Vector(1.1f)));
				}
				p.popStyle();
			}
			if (RuntimeParams.SHOW__LOCAL_BOUNDING_BOX) {
				// for current if show current in any case with interval
				// parameter
				if (isCurrentSceneElement() && RuntimeParams.SHOW__CURRENT) {
					if (((new Date()).getTime() % 1000) / 10 <= RuntimeParams.SHOW__BLINKING_INTERVAL) {
						draw_drawLocalBoundingBox_draw(p);
					}
				}
				// if not show current if relative box and bounding for current
				// type
				else if (draw_determineDrawBounding()) {
					draw_drawLocalBoundingBox_draw(p);
				}
			}
		}
		p.popStyle();
	}

	private void draw_drawLocalBoundingBox_draw(PApplet p) {
		p.pushStyle();
		{
			p.noFill();
			p.stroke(RuntimeParams.COLOR__DIRTY_YELLOW);
			PU.box(p, getLocalPosition(new Vector(1)));
		}
		p.popStyle();
	}

	private void draw_drawLocalCoordinateSystem(PApplet p) {
		if (RuntimeParams.SHOW__LOCAL_COORDINATE_SYSTEMS
				&& draw_determineDrawBounding()) {
			float width, height, depth;
			if (localDims != null) {
				width = localDims.getWidth() + .5f;
				height = localDims.getHeight() + .5f;
				depth = localDims.getDepth() + .5f;
			} else {
				width = 1.5f;
				height = 1.5f;
				depth = 1.5f;
			}
			CoordinateSystemDrawer.draw(p, -.5f, -.5f, -.5f, width, height,
					depth);
		}
	}

	private void draw_drawRotationOffsetBox(PApplet p) {
		if (draw_determineDrawBounding()) {
			if (RuntimeParams.SHOW__ROTATION_OFFSET_BOX) {
				p.pushStyle();
				{
					p.noFill();
					p.stroke(RuntimeParams.COLOR__GRAY);
					PU.box(p, getLocalPosition(new Vector(rotationOffset)));
				}
				p.popStyle();
			}
		}

	}

	private void draw_translateByRotationOffset(PApplet p) {
		PU.translate(p, rotationOffset);

	}

	protected Vector getLocalModelPosition(PApplet p, Vector factor) {
		Vector result;
		p.pushMatrix();
		{
			PU.translate(p, factor);
			result = new Vector(p.modelX(0, 0, 0), p.modelY(0, 0, 0), p.modelZ(
					0, 0, 0));
		}
		p.popMatrix();
		return result;
	}

	protected Vector getLocalPosition(Vector vector) {
		float factorX = vector.getX();
		float factorY = vector.getY();
		float factorZ = vector.getZ();
		return new Vector(localDims.getWidth() * factorX, localDims.getHeight()
				* factorY, localDims.getDepth() * factorZ);
	}

	protected boolean isCurrentSceneElement() {
		if (rootScene.isCurrentSceneElement(this)) {
			return true;
		} else {
			return false;
		}
	}

	protected boolean isLayout() {
		if (this instanceof GFX_Layout) {
			return true;
		} else {
			return false;
		}
	}

	protected boolean isNode() {
		if (this instanceof GFX_Node) {
			return true;
		} else {
			return false;
		}
	}

	protected boolean isTopLayout() {
		if (parentObject == rootScene) {
			return true;
		} else {
			return false;
		}
	}

	@Override
	public void log() {
		LogManager.logALL("<GFX_SceneElement>");
		LogManager.indent();
		{
			LogManager.logALL("parentObject = " + parentObject);
			// LogManager.logALL("rotation = " + rotation);
			LogManager.logALL("rotationOffset = " + rotationOffset);
			LogManager.logALL("localDimensions = " + localDims);
			super.log();
		}
		LogManager.deindent();
	}

	public GFX_Object navi_getParentObject() {
		return parentObject;
	}

}
